<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <h2>一、什么是双向绑定</h2>
  <div>当我们用JavaScript代码更新Model时，View就会自动更新。用户更新了View，Model的数据也自动被更新了，这种情况就是双向绑定</div>
  <h2>二、双向绑定的原理是什么</h2>
  <div>我们都知道 Vue 是数据双向绑定的框架，双向绑定由三个重要部分构成</div>
  <ul>
    <li>数据层（Model）：应用的数据及业务逻辑</li>
    <li>视图层（View）：应用的展示效果，各类UI组件</li>
    <li>业务逻辑层（ViewModel）：框架封装的核心，它负责将数据与视图关联起来</li>
  </ul>
  <h2>理解ViewModel</h2>
  <div>它还有两个主要部分组成</div>
  <ul>
    <li>监听器（Observer）：对所有数据的属性进行监听</li>
    <li>解析器（Compiler）：对每个元素节点的指令进行扫描跟解析,根据指令模板替换数据,以及绑定相应的更新函数</li>
  </ul>
  <h2>三、实现双向绑定</h2>
  <div>Vue中的双向绑定流程是什么的</div>
  <ul>
    <li>new Vue()首先执行初始化，对data执行响应化处理，这个过程发生Observe中</li>
    <li>同时对模板执行编译，找到其中动态绑定的数据，从data中获取并初始化视图，这个过程发生在Compile中</li>
    <li>同时定义⼀个更新函数和Watcher，将来对应数据变化时Watcher会调用更新函数</li>
    <li>由于data的某个key在⼀个视图中可能出现多次，所以每个key都需要⼀个管家Dep来管理多个Watcher</li>
    <li>将来data中数据⼀旦发生变化，会首先找到对应的Dep，通知所有Watcher执行更新函数</li>
  </ul>
  <h4>实现</h4>
  <div>初始化Observe</div>
  <br>
  <div>--new Vue()首先执行初始化，对data执行响应化处理</div>
  <script>
  // 先来一个构造函数：执行初始化，对data执行响应化处理
  //   class Vue {
  //     constructor(options) {
  //       this.$options = options;
  //       this.$data = options.data;
          
  //       // 对data选项做响应式处理
  //       observe(this.$data);
          
  //       // 代理data到vm上
  //       proxy(this);
          
  //       // 执行编译
  //       new Compile(options.el, this);
  //     }
  // }


  // 对data选项执行响应化具体操作
  // function observe(obj) {
  //   if (typeof obj !== "object" || obj == null) {
  //     return;
  //   }
  //   new Observer(obj);
  // }

  // class Observer {
  //   constructor(value) {
  //     this.value = value;
  //     this.walk(value);
  //   }
  //   walk(obj) {
  //     Object.keys(obj).forEach((key) => {
  //       defineReactive(obj, key, obj[key]);
  //     });
  //   }
  // }
  // </script>
  <br>
  <div>编译Compile</div>
  <br>
  <div>--对每个元素节点的指令进行扫描跟解析,根据指令模板替换数据,以及绑定相应的更新函数</div>
  <script>
    class Compile {
      constructor(el, vm) {
        this.$vm = vm;
        this.$el = document.querySelector(el);  // 获取dom
        if (this.$el) {
          this.compile(this.$el);
        }
      }
      compile(el) {
        const childNodes = el.childNodes; 
        Array.from(childNodes).forEach((node) => { // 遍历子元素
          if (this.isElement(node)) {   // 判断是否为节点
            console.log("编译元素" + node.nodeName);
          } else if (this.isInterpolation(node)) {
            console.log("编译插值⽂本" + node.textContent);  // 判断是否为插值文本 {{}}
          }
          if (node.childNodes && node.childNodes.length > 0) {  // 判断是否有子元素
            this.compile(node);  // 对子元素进行递归遍历
          }
        });
      }
      isElement(node) {
        return node.nodeType == 1;
      }
      isInterpolation(node) {
        return node.nodeType == 3 && /\{\{(.*)\}\}/.test(node.textContent);
      }
    }
  </script>
  <br>
  <div>依赖收集</div>
  <br>
  <div>--视图中会用到data中某key，这称为依赖。同⼀个key可能出现多次，每次都需要收集出来用⼀个Watcher来维护它们，此过程称为依赖收集</div>
  <br>
  <div>--多个Watcher需要⼀个Dep来管理，需要更新时由Dep统⼀通知</div>
  <br>
  <div>实现思路</div>
  <ul>
    <li>defineReactive时为每⼀个key创建⼀个Dep实例</li>
    <li>初始化视图时读取某个key，例如name1，创建⼀个watcher1</li>
    <li>于触发name1的getter方法，便将watcher1添加到name1对应的Dep中</li>
    <li>当name1更新，setter触发时，便可通过对应Dep通知其管理所有Watcher更新</li>
  </ul>
  <script>
    // 负责更新视图
    // class Watcher {
    //   constructor(vm, key, updater) {
    //     this.vm = vm
    //     this.key = key
    //     this.updaterFn = updater

    //     // 创建实例时，把当前实例指定到Dep.target静态属性上
    //     Dep.target = this
    //     // 读一下key，触发get
    //     vm[key]
    //     // 置空
    //     Dep.target = null
    //   }

    //   // 未来执行dom更新函数，由dep调用的
    //   update() {
    //     this.updaterFn.call(this.vm, this.vm[this.key])
    //   }
    // }

    // 声明Dep
    // class Dep {
    //   constructor() {
    //     this.deps = [];  // 依赖管理
    //   }
    //   addDep(dep) {
    //     this.deps.push(dep);
    //   }
    //   notify() { 
    //     this.deps.forEach((dep) => dep.update());
    //   }
    // }

    // 创建watcher时触发getter
    // class Watcher {
    //   constructor(vm, key, updateFn) {
    //     Dep.target = this;
    //     this.vm[this.key];
    //     Dep.target = null;
    //   }
    // }

    // 依赖收集，创建Dep实例
    // function defineReactive(obj, key, val) {
    //   this.observe(val);
    //   const dep = new Dep();
    //   Object.defineProperty(obj, key, {
    //     get() {
    //       Dep.target && dep.addDep(Dep.target);// Dep.target也就是Watcher实例
    //       return val;
    //     },
    //     set(newVal) {
    //       if (newVal === val) return;
    //       dep.notify(); // 通知dep执行更新方法
    //     },
    //   });
    // }
  </script>
</body>
</html>